/*


  Simple Demo for GLSL 2.0

  www.lighthouse3d.com

*/
#include <stdio.h>
#include <stdlib.h>

#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/glext.h>

#include "textfile.h"
#include "Noise3DTexture.h"

GLuint v,f,f2,p;
float lpos[4] = {1,0.5,1,0};
float rotationX = 0.0, rotationY = 0.0;
int   last_x, last_y;

int selectedShader = 1;

void setShaders();

void changeSize(int w, int h)
{
	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if(h == 0)
		h = 1;

	float ratio = 1.0* w / h;

	// Reset the coordinate system before modifying
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Set the viewport to be the entire window
   glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45,ratio,1,1000);
	glMatrixMode(GL_MODELVIEW);
}

void drawFPS()
{
  static char fpsBuf[128] = {0};
  static int frames = 0;
  static int t0 = 0;
  frames++;
  GLint t = glutGet(GLUT_ELAPSED_TIME);
  if (t - t0 >= 1000) {
    GLfloat seconds = (t - t0) / 1000.0;
    GLfloat fps = frames / seconds;
    sprintf(fpsBuf, "FPS: %.1f - Noise Shaders - ESC to exit - ", fps);
    t0 = t;
    frames = 0;
  }
  glutSetWindowTitle(fpsBuf);
}

void renderScene(void) {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();
	gluLookAt(0.0,0.0,4.0,  0.0,0.0,-1.0,  0.0f,1.0f,0.0f);

	glLightfv(GL_LIGHT0, GL_POSITION, lpos);

   glRotatef( rotationY, 0.0, 1.0, 0.0 );
  	glRotatef( rotationX, 1.0, 0.0, 0.0 );

	drawFPS();

	glutSolidTeapot(1);
	glutSwapBuffers();
}

GLint getUniLoc(GLuint program, const GLchar *name)
{
    GLint loc;

    loc = glGetUniformLocation(program, name);
    if (loc == -1)
        printf("No such uniform named \"%s\"\n", name);
    return loc;
}

int installNoiseShaders(const GLchar *NoiseVertex, const GLchar *NoiseFragment)
{
	GLuint NoiseVS, NoiseFS, NoiseProg;   // handles to objects
//   GLint  vertCompiled, fragCompiled;    // status values
//   GLint  linked;
	// Create a vertex shader object and a fragment shader object
	NoiseVS = glCreateShader(GL_VERTEX_SHADER);
	NoiseFS = glCreateShader(GL_FRAGMENT_SHADER);
	// Load source code strings into shaders
	glShaderSource(NoiseVS, 1, &NoiseVertex, NULL);
	glShaderSource(NoiseFS, 1, &NoiseFragment, NULL);
	// Compile the Noise vertex shader and print out
	// the compiler log file.
	glCompileShader(NoiseVS);
	glCompileShader(NoiseFS);
//	if (!vertCompiled || !fragCompiled)
//	  	 return 0;
	// Create a program object and attach the two compiled shaders
	NoiseProg = glCreateProgram();
	glAttachShader(NoiseProg, NoiseVS);
	glAttachShader(NoiseProg, NoiseFS);
	// Link the program object and print out the info log
	glLinkProgram(NoiseProg);
//	if (!linked)
//	    return 0;

	// Install program object as part of current state
	glUseProgram(NoiseProg);

   glUniform1f(getUniLoc(NoiseProg, "Scale"), 0.5);
   glUniform3f(getUniLoc(NoiseProg, "LightPos"), 1.0, 1.0, 1.0);
   glUniform1f(getUniLoc(NoiseProg, "Noise"), 0);

	switch(selectedShader)
	{
		case 1:
		   glUniform3f(getUniLoc(NoiseProg, "SkyColor"), 0.0, 0.0, 0.8);
		   glUniform3f(getUniLoc(NoiseProg, "CloudColor"),0.8, 0.8, 0.8);
		break;
		case 2:
		   glUniform3f(getUniLoc(NoiseProg, "Color1"), 0.8, 0.7, 0.0);
		   glUniform3f(getUniLoc(NoiseProg, "Color2"), 0.6, 0.1, 0.8);
		   glUniform1f(getUniLoc(NoiseProg, "NoiseScale"), 1.2);
		break;
		case 3:
		   glUniform3f(getUniLoc(NoiseProg, "MarbleColor"), 0.6, 0.7, 0.6);
		   glUniform3f(getUniLoc(NoiseProg, "VeinColor"), 0.1, 0.1, 0.1);
		break;
		case 4:
		   glUniform1f(getUniLoc(NoiseProg, "NoiseScale"), 1.2);
		break;
		case 5:
		   glUniform3f(getUniLoc(NoiseProg, "LightWood"), 0.6, 0.3, 0.1);
		   glUniform3f(getUniLoc(NoiseProg, "DarkWood"), 0.4, 0.2, 0.07);
		   glUniform3f(getUniLoc(NoiseProg, "NoiseScale"), 0.5, 0.1, 0.1);

		   glUniform1f(getUniLoc(NoiseProg, "RingFreq"), 4.0);
		   glUniform1f(getUniLoc(NoiseProg, "LightGrains"), 1.0);
		   glUniform1f(getUniLoc(NoiseProg, "DarkGrains"), 0.0);
		   glUniform1f(getUniLoc(NoiseProg, "GrainThreshold"), 0.5);
		   glUniform1f(getUniLoc(NoiseProg, "Noisiness"), 3.0);
		   glUniform1f(getUniLoc(NoiseProg, "GrainScale"), 27.0);

		   glUniform1f(getUniLoc(NoiseProg, "Scale"), 2.0);
		   glUniform3f(getUniLoc(NoiseProg, "LightPos"), 0.0,0.0,0.4);
			// Scale 2.0
			// LightPos  0.0, 0.0, 4.0
		break;

	}
   return 1;
}

void processNormalKeys(unsigned char key, int x, int y)
{
	if (key == 27)
		exit(0);
	int val = (int) key - 48;
	if(val >=1 && val <=5)
		selectedShader = val;
	setShaders();
	glutPostRedisplay();
}

void setShaders()
{
	char *vs = NULL,*fs = NULL;
	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);

	vs = textFileRead((char*) "../projetos/shaderNoise1/shaders/sun.vert");

	switch(selectedShader)
	{
		case 1: fs = textFileRead((char*) "../projetos/shaderNoise1/shaders/cloud.frag"); 		break;
		case 2: fs = textFileRead((char*) "../projetos/shaderNoise1/shaders/sun.frag"); 		break;
		case 3: fs = textFileRead((char*) "../projetos/shaderNoise1/shaders/marble.frag");		break;
		case 4: fs = textFileRead((char*) "../projetos/shaderNoise1/shaders/granite.frag");	break;
		case 5: fs = textFileRead((char*) "../projetos/shaderNoise1/shaders/wood.frag");	break;
		case 0: exit(0); break;
	}

	const char * ff = fs;
	const char * vv = vs;

	installNoiseShaders(vv, ff);
}

// Motion callback
void motion(int x, int y )
{
  rotationX += (float) (y - last_y);
  rotationY += (float) (x - last_x);

  last_x = x;
  last_y = y;
}

int main(int argc, char **argv) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(600,600);
	glutCreateWindow("Noise Shaders - ESC to change");

   glutMotionFunc( motion );
	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutKeyboardFunc(processNormalKeys);

	glEnable(GL_DEPTH_TEST);
	glClearColor(1.0,1.0,1.0,1.0);

	glewInit();
	if (glewIsSupported("GL_VERSION_2_0"))
		printf("Ready for OpenGL 2.0\n");
	else {
		printf("OpenGL 2.0 not supported\n");
		exit(1);
	}

	printf("\nUtilize o teclado numérico para selecionar os efeitos de: ");
	printf("\n1 - Nuvens");
	printf("\n2 - Superficie Solar");
	printf("\n3 - Marmore");
	printf("\n4 - Granito");
	printf("\n5 - Madeira");
	printf("\n\nPressione ESC para sair\n");

	setShaders();

	CreateNoise3D();

	glutMainLoop();

	// just for compatibiliy purposes
	return 0;
}

